using System;
using System.Linq.Expressions;
using System.Reflection;

namespace RedRiver.Framework.XtraCommon.FastReflection{
    public interface IFieldAccessor{
        object GetValue(object instance);
    }

    public class FieldAccessor : IFieldAccessor{
        private Func<object, object> _getter;

        public FieldAccessor(FieldInfo fieldInfo){
            FieldInfo = fieldInfo;
        }

        public FieldInfo FieldInfo{
            get;
            private set;
        }

        #region IFieldAccessor Members

        object IFieldAccessor.GetValue(object instance){
            return GetValue(instance);
        }

        #endregion

        private void InitializeGet(FieldInfo fieldInfo){
            // target: (object)(((TInstance)instance).Field)

            // preparing parameter, object type
            ParameterExpression instance = Expression.Parameter(typeof (object), "instance");

            // non-instance for static method, or ((TInstance)instance)
            UnaryExpression instanceCast = fieldInfo.IsStatic
                                               ? null
                                               :
                                                   Expression.Convert(instance, fieldInfo.ReflectedType);

            // ((TInstance)instance).Property
            MemberExpression fieldAccess = Expression.Field(instanceCast, fieldInfo);

            // (object)(((TInstance)instance).Property)
            UnaryExpression castFieldValue = Expression.Convert(fieldAccess, typeof (object));

            // Lambda expression
            Expression<Func<object, object>> lambda = Expression.Lambda<Func<object, object>>(castFieldValue, instance);

            _getter = lambda.Compile();
        }

        public object GetValue(object instance){
            return _getter(instance);
        }
    }
}